/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2020年8月27日
 */
package com.massyframework.beanskin.util.scanning;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 基于*.jar文件资源的扫描器
 * 
 * @author huangkh
 *
 */
public class JarFileResourceScanner implements ResourceScanner<JarFile> {

	private static final String PROTOCOL     = "jar";
    private static final String SEPARATOR    = "/";
    
    private Logger logger = LoggerFactory.getLogger(JarFileResourceScanner.class);
	/**
	 * 构造方法
	 */
	public JarFileResourceScanner() {
		
	}

	@Override
	public JarFile support(URL url) {
		if (PROTOCOL.equals(url.getProtocol())){
            try {
                JarURLConnection conn = (JarURLConnection) url.openConnection();
                return conn.getJarFile();
            }catch(Exception e){
                return null;
            }
        }else{
            if (url.getProtocol().equals("file")) {
                String path = url.getPath();
                if (path.endsWith(".jar")) {
                    try {
                        return new JarFile(new File(path));
                    } catch (IOException e) {
                        return null;
                    }
                }
            }
        }

        return null;
	}

	@Override
	public void scanning(JarFile instance, String resourceDir, Predicate<String> filter, boolean subdirectory,
			ResourceScanningCallback callback) {
		Objects.requireNonNull(instance, "\"instance\" cannot be null.");
		Objects.requireNonNull(resourceDir, "\"resourceDir\" cannot be null.");
		Objects.requireNonNull(filter, "\"filter\" cannot be null.");
		Objects.requireNonNull(callback, "\"callback\" cannot be null.");
		String path = resourceDir.startsWith(SEPARATOR) ?
                resourceDir.substring(1) : resourceDir;
        if (!path.endsWith(SEPARATOR)){
            path = path.concat(SEPARATOR);
        }

        int length = path.length();
        
        Enumeration<JarEntry> em = instance.entries();
        while (em.hasMoreElements()){
            JarEntry entry = em.nextElement();
            String name = entry.getName();
            if (name.startsWith(path)){
            	String filePath = name.substring(length);
                if (filePath.length() == 0) continue;
                
                if (!subdirectory){
                    if (filePath.indexOf(SEPARATOR) != -1){
                        continue;
                    }
                }
                
                if (this.doFilter(filePath, filter)){
                    String uri = "jar:file:".concat(instance.getName()).concat("!/").concat(name);
                    try {
                        callback.onFound(filePath, new URL(uri));
                    } catch (MalformedURLException e) {
                    	if (logger.isErrorEnabled()) {
                    		logger.error(e.getMessage(), e);
                    	}
                    }
                }
            }
        }		
	}

	/**
     * 执行过滤
     * @param filePath {@link String}, 资源的相对路径
     * @param filter {@link Predicate}, 过滤器
     * @return {@link boolean}, 满足条件则返回<code>true</code>
     */
    protected boolean doFilter(String filePath, Predicate<String> filter){
        return filter.test(filePath);
    }

}
